<?php
declare(strict_types = 1);

namespace App\Model\Logic;


use App\Helper\RedisConst;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Bean\Annotation\Mapping\Inject;
use Swoft\Redis\Pool;

/**
 * @Bean()
 * Class YunXinLogic
 * @package App\Model\Logic
 */
class YunXinLogic
{

    /**
     * @Inject()
     * @var MemberRedisLogic
     */
    private $member_redis;

    /**
     * @Inject()
     * @var Pool
     */
    private $redis;

    /**
     * @Inject()
     * @var AesLogic
     */
    private $aes_logic;

    private $AppKey;                //开发者平台分配的AppKey
    private $AppSecret;             //开发者平台分配的AppSecret,可刷新
    private $Nonce;                    //随机数（最大长度128个字符）
    private $CurTime;              //当前UTC时间戳，从1970年1月1日0点0 分0 秒开始到现在的秒数(String)
    private $CheckSum;             //SHA1(AppSecret + Nonce + CurTime),三个参数拼接的字符串，进行SHA1哈希计算，转化成16进制字符(String，小写)
    const   HEX_DIGITS = "0123456789abcdef";

    /**
     * 参数初始化
     * @param $AppKey
     * @param $AppSecret
     * @param $RequestType [选择php请求方式，fsockopen或curl,若为curl方式，请检查php配置是否开启]
     */
    public function __construct()
    {
        $this->AppKey = env('YUNXIN_KEY', '25116b4de1b7c94047d0033c7e5eb4ec');
        $this->AppSecret = env('YUNXIN_SECRET', '11324414ed18');
    }

    /**
     * API checksum校验生成
     * @param  void
     * @return $CheckSum(对象私有属性)
     */
    public function checkSumBuilder()
    {
        //此部分生成随机字符串
        $hex_digits = self::HEX_DIGITS;
        $this->Nonce;
        for ($i = 0; $i < 128; $i++) {          //随机字符串最大128个字符，也可以小于该数
            $this->Nonce .= $hex_digits[rand(0, 15)];
        }
        $this->CurTime = (string)(time()); //当前时间戳，以秒为单位

        $join_string = $this->AppSecret . $this->Nonce . $this->CurTime;
        $this->CheckSum = sha1($join_string);
        //print_r($this->CheckSum);
    }


    /**
     * 使用CURL方式发送post请求
     * @param  $url [请求地址]
     * @param  $data [array格式数据]
     * @return $请求返回结果(array)
     */
    public function postDataCurl($url, $data)
    {
        $this->checkSumBuilder();      //发送请求前需先生成checkSum

        $timeout = 5000;
        $http_header = array(
            'AppKey:' . $this->AppKey,
            'Nonce:' . $this->Nonce,
            'CurTime:' . $this->CurTime,
            'CheckSum:' . $this->CheckSum,
            'Content-Type:application/x-www-form-urlencoded;charset=utf-8'
        );
        $postdata = http_build_query($data);
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $postdata);
        curl_setopt($ch, CURLOPT_HEADER, false);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $http_header);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //处理http证书问题
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        $result = curl_exec($ch);
        if (false === $result) {
            $result = curl_errno($ch);
        }
        curl_close($ch);
        return json_decode($result, true);
    }


    /**
     * 创建云信ID
     * 1.第三方帐号导入到云信平台；
     * 2.注意accid，name长度以及考虑管理秘钥token
     * @param  $accid [云信ID，最大长度32字节，必须保证一个APP内唯一（只允许字母、数字、半角下划线_、@、半角点以及半角-组成，不区分大小写，会统一小写处理）]
     * @param  $name [云信ID昵称，最大长度64字节，用来PUSH推送时显示的昵称]
     * @param  $props [json属性，第三方可选填，最大长度1024字节]
     * @param  $avatar [云信ID头像URL，第三方可选填，最大长度1024]
     * @param  $token [云信ID可以指定登录token值，最大长度128字节，并更新，如果未指定，会自动生成token，并在创建成功后返回]
     * @return $result    [返回array数组对象]
     */
    public function createUserId($accid, $name = '', $avatar = '', $token='')
    {
        $url = 'https://api.netease.im/nimserver/user/create.action';
        $data = array(
            'accid' => $accid,
            'name'  => $name,
            'icon'  => $avatar,
        );
        if($token) $data['token'] = $token;
        $result = $this->postDataCurl($url, $data);
        return $result;
    }


    /**
     * 更新云信ID
     * @param  $accid     [云信ID，最大长度32字节，必须保证一个APP内唯一（只允许字母、数字、半角下划线_、@、半角点以及半角-组成，不区分大小写，会统一小写处理）]
     * @param  $name      [云信ID昵称，最大长度64字节，用来PUSH推送时显示的昵称]
     * @param  $props     [json属性，第三方可选填，最大长度1024字节]
     * @param  $token     [云信ID可以指定登录token值，最大长度128字节，并更新，如果未指定，会自动生成token，并在创建成功后返回]
     * @return $result    [返回array数组对象]
     */
    public function refreshUserId($accid, $name='', $avatar='', $token='', $props='{}'){
        $url = 'https://api.netease.im/nimserver/user/refreshToken.action';
        $data= array(
            'accid' => $accid,
            'name'  => $name,
            'icon' => str_replace('preview','100x100', $avatar),
            'props' => $props,
            'token' => $token,
        );
        $result = $this->postDataCurl($url,$data);
        return $result;
    }


    /**
     * 更新用户名片
     * @param  $accid       [云信ID，最大长度32字节，必须保证一个APP内唯一（只允许字母、数字、半角下划线_、@、半角点以及半角-组成，不区分大小写，会统一小写处理）]
     * @param  $name        [云信ID昵称，最大长度64字节，用来PUSH推送时显示的昵称]
     * @param  $icon        [用户icon，最大长度256字节]
     * @param  $sign        [用户签名，最大长度256字节]
     * @param  $email       [用户email，最大长度64字节]
     * @param  $birth       [用户生日，最大长度16字节]
     * @param  $mobile      [用户mobile，最大长度32字节]
     * @param  $ex          [用户名片扩展字段，最大长度1024字节，用户可自行扩展，建议封装成JSON字符串]
     * @param  $gender      [用户性别，0表示未知，1表示男，2女表示女，其它会报参数错误]
     * @return $result      [返回array数组对象]
     */
    public function updateUinfo($accid,$name='',$icon='',$sign='',$email='',$birth='',$mobile='',$gender='0',$ex=''){
        try{
            $url = 'https://api.netease.im/nimserver/user/updateUinfo.action';
            $data= array(
                'accid' => $accid,
                'name' => $name,
                'icon' => $icon,
                'sign' => $sign,
                'email' => $email,
                'birth' => $birth,
                'mobile' => $mobile,
                'gender' => $gender,
                'ex' => $ex
            );
            $result = $this->postDataCurl($url,$data);
            return $result;

        }catch (\Exception $e){
            echo $e->getMessage();
        }
    }



    public function updateUinfonArr($accid,$user_info = []){
        try{
            $url = 'https://api.netease.im/nimserver/user/updateUinfo.action';
            $data= array(
                'accid' => $accid,
            );
            if(isset($user_info['name'])) $data['name'] = $user_info['name'];
            if(isset($user_info['icon'])) $data['icon'] = $user_info['icon'];
            if(isset($user_info['sign'])) $data['sign'] = $user_info['sign'];
            if(isset($user_info['email'])) $data['email'] = $user_info['email'];
            if(isset($user_info['birth'])) $data['birth'] = $user_info['birth'];
            if(isset($user_info['mobile'])) $data['mobile'] = $user_info['mobile'];
            if(isset($user_info['gender'])) $data['gender'] = $user_info['gender'];
            if(isset($user_info['ex'])) $data['ex'] = $user_info['ex'];
            $result = $this->postDataCurl($url,$data);
            return $result;

        }catch (\Exception $e){
            echo $e->getMessage();
        }
    }


    /**
     * 获取用户名片，可批量
     * @param  $accids    [用户帐号（例如：JSONArray对应的accid串，如："zhangsan"，如果解析出错，会报414）（一次查询最多为200）]
     * @return $result    [返回array数组对象]
     */
    public function getUinfos($accids){
        $url = 'https://api.netease.im/nimserver/user/getUinfos.action';
        $data= array(
            'accids' => json_encode($accids)
        );
        $result = $this->postDataCurl($url,$data);
        return $result;
    }

    /**
     * 消息功能-发送普通消息
     * @param  $from       [发送者accid，用户帐号，最大32字节，APP内唯一]
     * @param  $ope        [0：点对点个人消息，1：群消息，其他返回414]
     * @param  $to        [ope==0是表示accid，ope==1表示tid]
     * @param  $type        [0 表示文本消息,1 表示图片，2 表示语音，3 表示视频，4 表示地理位置信息，6 表示文件，100 自定义消息类型]
     * @param  $body       [请参考下方消息示例说明中对应消息的body字段。最大长度5000字节，为一个json字段。]
     * @param  $option       [发消息时特殊指定的行为选项,Json格式，可用于指定消息的漫游，存云端历史，发送方多端同步，推送，消息抄送等特殊行为;option中字段不填时表示默认值]
     * @param  $pushcontent      [推送内容，发送消息（文本消息除外，type=0），option选项中允许推送（push=true），此字段可以指定推送内容。 最长200字节]
     */
    public function sendMsg($from,$ope,$to,$type,$body,$option='{"push":true,"roam":true,"history":false,"sendersync":true, "route":false}',$pushcontent='', $payload = ''){
        $url = 'https://api.netease.im/nimserver/msg/sendMsg.action';
        $data= array(
            'from' => $from,
            'ope' => $ope,
            'to' => $to,
            'type' => $type,
            'body' => json_encode($body),
            'option' => $option,
            'pushcontent' => $pushcontent,
            'payload' => $payload
        );
        $result = $this->postDataCurl($url,$data);
        return $result;
    }


    /**
     * 获取历史记录
     * @param $from
     * @param $to
     * @param $begintime
     * @param $endtime
     * @param int $limit
     * @return mixed
     */
    public function history($from,$to,$begintime, $endtime, $limit=100){
        $url = 'https://api.netease.im/nimserver/history/querySessionMsg.action';
        $data= array(
            'from' => $from,
            'to' => $to,
            'begintime' => $begintime *1000,
            'endtime' => $endtime*1000,
            'limit' => $limit
        );
        $result = $this->postDataCurl($url,$data);
        return $result;
    }

    /**
     * --------------------------------------------------------------------------
     * 发送自定义系统通知
     * --------------------------------------------------------------------------
     * @param $from 发送者accid，用户帐号，最大32字符，APP内唯一
     * @param int $msgtype 0：点对点自定义通知，1：群消息自定义通知，其他返回414
     * @param $to msgtype==0是表示accid即用户id，msgtype==1表示tid即群id
     * @param $attach 自定义通知内容，第三方组装的字符串，建议是JSON串，最大长度4096字符
     * @return mixed
     */
    public function sendAttachMsg($from, $to, $attach, $msgtype =0)
    {
        $url = 'https://api.netease.im/nimserver/msg/sendAttachMsg.action';
        $data= array(
            'from' => $from,
            'to' => $to,
            'msgtype' => $msgtype,
            'attach' => $attach,
        );
        $result = $this->postDataCurl($url,$data);
        return $result;
    }


    /**
     * 创建云信id
     * @param $member_id
     * @return array|mixed
     */
    public function yunXinID($member_id)
    {
        $info = $this->member_redis->info($member_id);
        $accid = buildAccID($member_id, $info['type']);
        if($info['type'] != 1) $info['nickname'] = $info['displayname'];
        $user = $this->redis->get(RedisConst::YUNXIN.$accid);
        if($user) {
            $t = json_decode($user, true);
            if(isset($t['uuid'])) return json_decode($user, true);
            $conf = [ 1=> 'normal', 2=> 'brand', 3 => 'shop' ];
            $ex = [ 'uuid' => $info['uuid'], 'type' => $conf[$info['type']], 'business_id' => $info['type_id'] ];
            $update = [ 'ex' => JSON($ex) ];
            $this->updateUinfonArr($accid, $update);
            $uinfon = array_merge($t, $ex);
            $this->redis->set(RedisConst::YUNXIN.$accid, json_encode($uinfon));
            return $uinfon;
        }
        $info['nickname'] = substr($info['nickname'], 0 ,60);
        $res =  $this->createUserId($accid, $info['nickname'], $info['avatar']);
        //注册
        if(isset($res['code']) && $res['code'] == 200){
            $token = $res['info']['token'];
        } else {
            $tmp_return = $this->updateYunxinToken($member_id);
            //云信 token的获取， 可能存在问题
            $token = !is_numeric($tmp_return) ? $tmp_return['token'] : "";
        }
        //设置缓存数据
        //更新用户名片 uuid
        $conf = [ 1=> 'normal', 2=> 'brand', 3 => 'shop' ];
        $ex = [ 'uuid' => $info['uuid'], 'type' => $conf[$info['type']], 'business_id' => $info['type_id'] ];
        $update = [
            'ex' => JSON($ex),
        ];
        $this->updateUinfonArr($accid, $update);
        $user = [
            'accid' => $accid,
            'token' => $token,
        ];
        $uinfon = array_merge($user, $ex);
        $this->redis->set(RedisConst::YUNXIN.$accid, json_encode($uinfon));
        return $uinfon;
    }


    //更新 update token
    public function updateYunxinToken($member_id)
    {
        $info = $this->member_redis->info($member_id);
        //普通用户
        $accid = env('APP_ENV').'_'.$member_id;
        //品牌用户
        if($info['type'] == 2){
            $accid = env('APP_ENV').'_'.$member_id ."_brand";
        }
        //店铺用户
        if($info['type'] == 3){
            $accid = env('APP_ENV').'_'.$member_id ."_shop";
        }
        $aes_key = config("app.aes_key");
        $token = $this->aes_logic->AESEncryptRequest($aes_key, $accid);
        $token = md5($token);
        $res = $this->refreshUserId($accid, $info['nickname'], $info['avatar'], $token);
        if(isset($res['code']) && $res['code'] == 200){
            $user = [
                'accid' => $accid,
                'token' => $res['info']['token'],
            ];
            $this->redis->set(RedisConst::YUNXIN.$accid, json_encode($user));
            return $user;
        }elseif(isset($res['code']) && $res['code'] == 414){
            $this->createUserId($accid, $info['nickname'], $info['avatar'], $token);
            $user = [
                'accid' => $accid,
                'token' => $token,
            ];
            $this->redis->set(RedisConst::YUNXIN.$accid, json_encode($user));
            return $user;
        }else{
            return 10001;
        }
    }

}